package core

type Error struct {
	Message string
}

// Type returns the object type as a string
func (e *Error) Type() ObjectType {
	return ERROR_OBJ
}

// String returns the error message
func (e *Error) Inspect() string {
	return "ERROR: " + e.Message
}
func Not(args []Object, env *Environment) Object {
	if len(args) != 1 {
		return &Error{Message: "not requires exactly 1 argument"}
	}

	b := true
	arg := args[0]
	// In Scheme, anything that is not #f is considered true
	if arg.Type() == BOOLEAN_OBJ {
		b = arg.(*BooleanObject).Value
	}
	return &BooleanObject{Value: go_not(b)}
}

func go_not(b bool) bool {
	return !b
}

func Or(args []Object, env *Environment) Object {
	// In Scheme, (or) with no arguments returns #f
	//if len(args) == 0 {
	//	return &BooleanObject{Value: false}
	//}
	//
	//// Evaluate each argument and return #t if any is not #f, otherwise return #f
	//for _, arg := range args {
	//	// In Scheme, only #f is false, everything else is true
	//	if !(arg.Type() == BOOLEAN_OBJ && arg.(*BooleanObject).Value == false) {
	//		return &BooleanObject{Value: true}
	//	}
	//}
	//
	//return &BooleanObject{Value: false}
	bs := make([]bool, len(args))
	for i, arg := range args {
		if arg.Type() != BOOLEAN_OBJ {
			bs[i] = true
		} else {
			bs[i] = arg.(*BooleanObject).Value
		}
	}
	return &BooleanObject{go_or(bs)}
}

func go_or(bs []bool) bool {
	for _, b := range bs {
		if b {
			return true
		}
	}
	return false
}

func And(args []Object, env *Environment) Object {
	// In Scheme, (and) with no arguments returns #t
	//if len(args) == 0 {
	//	return &BooleanObject{Value: true}
	//}
	//
	//// Evaluate each argument and return #f if any is #f, otherwise return #t
	//for _, arg := range args {
	//	// In Scheme, only #f is false, everything else is true
	//	if arg.Type() == BOOLEAN_OBJ && arg.(*BooleanObject).Value == false {
	//		return &BooleanObject{Value: false}
	//	}
	//}
	//
	//return &BooleanObject{Value: true}
	bs := make([]bool, len(args))
	for i, arg := range args {
		if arg.Type() != BOOLEAN_OBJ {
			bs[i] = true
		} else {
			bs[i] = arg.(*BooleanObject).Value
		}
	}
	return &BooleanObject{go_and(bs)}
}

func go_and(bs []bool) bool {
	for _, b := range bs {
		if !b {
			return false
		}
	}
	return true
}

func RegisterBuiltinProcedures(globalEnv *Environment) {
	// Register arithmetic operations
	globalEnv.Set("not", &Builtin{Fn: Not})
	globalEnv.Set("and", &Builtin{Fn: And})
	globalEnv.Set("or", &Builtin{Fn: Or})
}
